//
//  Environment.cpp
//  AntSimulation
//
//  Created by Robert Mayr on 24.10.12.
//  Copyright (c) 2012 Robert Mayr. All rights reserved.
//

#include "Environment.h"
#include "Creator.h"

// statische Variablen initialisieren, ansonsten in Scope nicht nutzbar
Environment* Environment::pInstance = NULL;
int Environment::nCurM = 0;
int Environment::nCurN = 0;
int Environment::nAreaCounter =0;
Area* Environment::pEntry = NULL;

Environment::Environment() {};

Environment::~Environment() {};

Environment* Environment::getInstance(int m, int n) {
    // Environment erstellen falls noch nicht erledigt
    if(pInstance == NULL) {
        pInstance = new Environment();
    }
    // Entry-point erstellen falls noch nicht erledigt
    if(pEntry == NULL) {
        pEntry = Creator::Create_Area();
    }
    if(m!=0 && n!=0) {
        // keine Groessenevaluierung falls 0 im Parameter. Gewuenschter Aufruf ohne Evaluierung!
        if(nCurM<=m && nCurN<=n) {
            // Expand falls aktuelle Groesse nicht gleich gewuenschter Groesse
            pInstance->Expand(m,n);
        }
    }
    return (pInstance);
}

int Environment::getCurM() {
    return nCurM;
}

int Environment::getCurN() {
    return nCurN;
}

void Environment::Expand(int m, int n) {
    if(m==0 || n==0) return; // nothing doable
    if(m==nCurM && n==nCurN) return; // nothing todo - size fits already
    
    // Expand
    // get starting point
    Area* pArea = pEntry;
    // Ersten in Zeile merken
    Area* pFirstInRow = pEntry;
    if(pArea == NULL) return; // entry-point seems to be garbage
    // Spalten erweitern, wenn noetig
    for (int jj=1; jj<n; jj++) {
        // nach Osten gehen, sofern moeglich
        if(pArea->get_east() !=NULL) {
            // weitergehen
            pArea = pArea->get_east();
        } else {
            // Osten erzeugen
            Area* pAreaNew = Creator::Create_Area();
            // Area einfuegen
            AddHorizontal(pArea,pAreaNew);
            // weitergehen
            pArea = pAreaNew;
        }
    }
    for (int ii=1; ii<m; ii++) {
    	// Zeile erweitern, wenn noetig
        // Zeiger auf ersten in Zeile setzen
        pArea = pFirstInRow;
        // nach Sueden gehen, sofern moeglich
    	if(pArea->get_south() != NULL) {
            // weitergehen
    		pArea = pArea->get_south();
            // ersten in Zeile merken
            pFirstInRow = pArea;
    	} else {
            // Sueden erzeugen
    		Area* pAreaNew = Creator::Create_Area();
            // Area einfuegen
            AddVertical(pArea, pAreaNew);
            // ersten in Zeile merken
            pFirstInRow = pAreaNew;
            // weitergehen
            pArea = pAreaNew;
    	}
        // Spalten erweitern, wenn noetig
    	for (int jj=1; jj<n; jj++) {
            // nach Osten gehen, sofern moeglich
    		if(pArea->get_east() !=NULL) {
                // weitergehen
    			pArea = pArea->get_east();
    		} else {
                // Osten erzeugen
    			Area* pAreaNew = Creator::Create_Area();
                // Area einfuegen
                AddHorizontal(pArea,pAreaNew);
                // weitergehen
                pArea = pAreaNew;
    		}
    	}
    }
    // set new size
    nCurM = m;
    nCurN = n;
}

void Environment::AddHorizontal(Area* pwest, Area* peast) {
    // set west in new
    peast->set_west(pwest);
    // east in western neighbor
    pwest->set_east(peast);
    // vom westlichen Knoten nach Norden
    Area* pTemp = pwest->get_north();
    if(pTemp !=NULL) {
        // vom Norden in den Osten, direkt noerdlich der neuen Area
        pTemp = pTemp->get_east();
        if(pTemp != NULL) {
            // set north in new
            peast->set_north(pTemp);
            // south in northern neighbor
            pTemp->set_south(peast);
        }
    }
}

void Environment::AddVertical(Area* pnorth, Area* psouth) {
    // set north in new
    psouth->set_north(pnorth);
    // set south in old
    pnorth->set_south(psouth);    
}

void Environment::PrintEnvironment() {
    // Zeiger auf Einstiegspunkt setzen
    Area* pArea = pEntry;
    // Zeiger auf ersten in der Reihe merken
    Area* pFirstInRow = pEntry;
    while (pArea != NULL) {
        // Area ausgeben
        pArea->PrintInfo();
        // nach Osten bewegen, sofern möglich
        pArea = pArea->get_east();
        if(pArea == NULL) {
            // Osten ist NULL, in nächste Zeile wechseln
            pArea = pFirstInRow->get_south();
            pFirstInRow = pArea;
        }
    }
}

void Environment::PrintEnvironmentViewable() {
    // Zeiger auf Einstiegspunkt setzen
    Area* pArea = pEntry;
    // Zeiger auf ersten in der Reihe merken
    Area* pFirstInRow = pEntry;
    while (pArea != NULL) {
        // Ausgabe Nord-Informationen
        while (pArea != NULL) {
            // Area ausgeben
            pArea->PrintViewable('n');
            // nach Osten bewegen, sofern möglich
            pArea = pArea->get_east();

        }
        std::cout << "\n";
        // Ausgabe Center-Informationen
        pArea = pFirstInRow;
        while (pArea != NULL) {
            // Area ausgeben
            pArea->PrintViewable('c');
            // nach Osten bewegen, sofern möglich
            pArea = pArea->get_east();
            
        }
        std::cout << "\n";
        // Ausgabe Sued-Informationen
        pArea = pFirstInRow;
        while (pArea != NULL) {
            // Area ausgeben
            pArea->PrintViewable('s');
            // nach Osten bewegen, sofern möglich
            pArea = pArea->get_east();
            
        }
        std::cout << "\n";
        // Naechste Zeile
        if(pArea == NULL) {
            // Osten ist NULL, in nächste Zeile wechseln
            pArea = pFirstInRow->get_south();
            pFirstInRow = pArea;
        }
    }
}







